('use strict');
function keyboard (value) {
  let key = {};
  key.value = value;
  key.isDown = false;
  key.isUp = true;
  key.press = undefined;
  key.release = undefined;
  //The `downHandler`
  key.downHandler = event => {
    if (event.key.toLocaleLowerCase() === key.value) {
      if (key.isUp && key.press) key.press();
      key.isDown = true;
      key.isUp = false;
      event.preventDefault();
    }
  };

  //The `upHandler`
  key.upHandler = event => {
    if (event.key.toLocaleLowerCase() === key.value) {
      if (key.isDown && key.release) key.release();
      key.isDown = false;
      key.isUp = true;
      event.preventDefault();
    }
  };

  //Attach event listeners
  const downListener = key.downHandler.bind(key);
  const upListener = key.upHandler.bind(key);

  window.addEventListener('keydown', downListener, false);
  window.addEventListener('keyup', upListener, false);

  // Detach event listeners
  key.unsubscribe = () => {
    window.removeEventListener('keydown', downListener);
    window.removeEventListener('keyup', upListener);
  };

  return key;
}
function hitTestRectangle (r1, r2) {
  //Define the variables we'll need to calculate
  let hit, combinedHalfWidths, combinedHalfHeights, vx, vy;

  //hit will determine whether there's a collision
  hit = false;

  //Find the center points of each sprite
  r1.centerX = r1.x + r1.width / 2;
  r1.centerY = r1.y + r1.height / 2;
  r2.centerX = r2.x + r2.width / 2;
  r2.centerY = r2.y + r2.height / 2;

  //Find the half-widths and half-heights of each sprite
  r1.halfWidth = r1.width / 2;
  r1.halfHeight = r1.height / 2;
  r2.halfWidth = r2.width / 2;
  r2.halfHeight = r2.height / 2;

  //Calculate the distance vector between the sprites
  vx = r1.centerX - r2.centerX;
  vy = r1.centerY - r2.centerY;

  //Figure out the combined half-widths and half-heights
  combinedHalfWidths = r1.halfWidth + r2.halfWidth;
  combinedHalfHeights = r1.halfHeight + r2.halfHeight;

  //Check for a collision on the x axis
  if (Math.abs(vx) < combinedHalfWidths) {
    //A collision might be occurring. Check for a collision on the y axis
    if (Math.abs(vy) < combinedHalfHeights) {
      //There's definitely a collision happening
      hit = true;
    } else {
      //There's no collision on the y axis
      hit = false;
    }
  } else {
    //There's no collision on the x axis
    hit = false;
  }

  //`hit` will be either `true` or `false`
  return hit;
}

var Bump = (function () {
  var _createClass = (function () {
    function defineProperties (target, props) {
      for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ('value' in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
      }
    }
    return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps);
      if (staticProps) defineProperties(Constructor, staticProps);
      return Constructor;
    };
  })();

  function _classCallCheck (instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError('Cannot call a class as a function');
    }
  }
  function Bump () {
    var renderingEngine = arguments.length <= 0 || arguments[0] === undefined ? PIXI : arguments[0];

    _classCallCheck(this, Bump);

    if (renderingEngine === undefined)
      throw new Error('Please assign a rendering engine in the constructor before using bump.js');

    this.renderer = 'pixi';
  }

  //`addCollisionProperties` adds extra properties to sprites to help
  //simplify the collision code. It won't add these properties if they
  //already exist on the sprite. After these properties have been
  //added, this methods adds a Boolean property to the sprite called `_bumpPropertiesAdded`
  //and sets it to `true` to flag that the sprite has these
  //new properties

  _createClass(Bump, [
    {
      key: 'addCollisionProperties',
      value: function addCollisionProperties (sprite) {
        //Add properties to Pixi sprites
        if (this.renderer === 'pixi') {
          //gx
          if (sprite.gx === undefined) {
            Object.defineProperty(sprite, 'gx', {
              get: function get () {
                return sprite.getGlobalPosition().x;
              },

              enumerable: true,
              configurable: true
            });
          }

          //gy
          if (sprite.gy === undefined) {
            Object.defineProperty(sprite, 'gy', {
              get: function get () {
                return sprite.getGlobalPosition().y;
              },

              enumerable: true,
              configurable: true
            });
          }

          //centerX
          if (sprite.centerX === undefined) {
            Object.defineProperty(sprite, 'centerX', {
              get: function get () {
                return sprite.x + sprite.width / 2;
              },

              enumerable: true,
              configurable: true
            });
          }

          //centerY
          if (sprite.centerY === undefined) {
            Object.defineProperty(sprite, 'centerY', {
              get: function get () {
                return sprite.y + sprite.height / 2;
              },

              enumerable: true,
              configurable: true
            });
          }

          //halfWidth
          if (sprite.halfWidth === undefined) {
            Object.defineProperty(sprite, 'halfWidth', {
              get: function get () {
                return sprite.width / 2;
              },

              enumerable: true,
              configurable: true
            });
          }

          //halfHeight
          if (sprite.halfHeight === undefined) {
            Object.defineProperty(sprite, 'halfHeight', {
              get: function get () {
                return sprite.height / 2;
              },

              enumerable: true,
              configurable: true
            });
          }

          //xAnchorOffset
          if (sprite.xAnchorOffset === undefined) {
            Object.defineProperty(sprite, 'xAnchorOffset', {
              get: function get () {
                if (sprite.anchor !== undefined) {
                  return sprite.width * sprite.anchor.x;
                } else {
                  return 0;
                }
              },

              enumerable: true,
              configurable: true
            });
          }

          //yAnchorOffset
          if (sprite.yAnchorOffset === undefined) {
            Object.defineProperty(sprite, 'yAnchorOffset', {
              get: function get () {
                if (sprite.anchor !== undefined) {
                  return sprite.height * sprite.anchor.y;
                } else {
                  return 0;
                }
              },

              enumerable: true,
              configurable: true
            });
          }

          if (sprite.circular && sprite.radius === undefined) {
            Object.defineProperty(sprite, 'radius', {
              get: function get () {
                return sprite.width / 2;
              },

              enumerable: true,
              configurable: true
            });
          }

          //Earlier code - not needed now.
          /*
        Object.defineProperties(sprite, {
          "gx": {
            get(){return sprite.getGlobalPosition().x},
            enumerable: true, configurable: true
          },
          "gy": {
            get(){return sprite.getGlobalPosition().y},
            enumerable: true, configurable: true
          },
          "centerX": {
            get(){return sprite.x + sprite.width / 2},
            enumerable: true, configurable: true
          },
          "centerY": {
            get(){return sprite.y + sprite.height / 2},
            enumerable: true, configurable: true
          },
          "halfWidth": {
            get(){return sprite.width / 2},
            enumerable: true, configurable: true
          },
          "halfHeight": {
            get(){return sprite.height / 2},
            enumerable: true, configurable: true
          },
          "xAnchorOffset": {
            get(){
              if (sprite.anchor !== undefined) {
                return sprite.height * sprite.anchor.x;
              } else {
                return 0;
              }
            },
            enumerable: true, configurable: true
          },
          "yAnchorOffset": {
            get(){
              if (sprite.anchor !== undefined) {
                return sprite.width * sprite.anchor.y;
              } else {
                return 0;
              }
            },
            enumerable: true, configurable: true
          }
        });
        */
        }

        //Add a Boolean `_bumpPropertiesAdded` property to the sprite to flag it
        //as having these new properties
        sprite._bumpPropertiesAdded = true;
      }

      /*
    hitTestPoint
    ------------
     Use it to find out if a point is touching a circlular or rectangular sprite.
    Parameters: 
    a. An object with `x` and `y` properties.
    b. A sprite object with `x`, `y`, `centerX` and `centerY` properties.
    If the sprite has a `radius` property, the function will interpret
    the shape as a circle.
    */
    },
    {
      key: 'hitTestPoint',
      value: function hitTestPoint (point, sprite) {
        //Add collision properties
        if (!sprite._bumpPropertiesAdded) this.addCollisionProperties(sprite);

        var shape = undefined,
          left = undefined,
          right = undefined,
          top = undefined,
          bottom = undefined,
          vx = undefined,
          vy = undefined,
          magnitude = undefined,
          hit = undefined;

        //Find out if the sprite is rectangular or circular depending
        //on whether it has a `radius` property
        if (sprite.radius) {
          shape = 'circle';
        } else {
          shape = 'rectangle';
        }

        //Rectangle
        if (shape === 'rectangle') {
          //Get the position of the sprite's edges
          left = sprite.x - sprite.xAnchorOffset;
          right = sprite.x + sprite.width - sprite.xAnchorOffset;
          top = sprite.y - sprite.yAnchorOffset;
          bottom = sprite.y + sprite.height - sprite.yAnchorOffset;

          //Find out if the point is intersecting the rectangle
          hit = point.x > left && point.x < right && point.y > top && point.y < bottom;
        }

        //Circle
        if (shape === 'circle') {
          //Find the distance between the point and the
          //center of the circle
          var _vx = point.x - sprite.x - sprite.width / 2 + sprite.xAnchorOffset,
            _vy = point.y - sprite.y - sprite.height / 2 + sprite.yAnchorOffset,
            _magnitude = Math.sqrt(_vx * _vx + _vy * _vy);

          //The point is intersecting the circle if the magnitude
          //(distance) is less than the circle's radius
          hit = _magnitude < sprite.radius;
        }

        //`hit` will be either `true` or `false`
        return hit;
      }

      /*
    hitTestCircle
    -------------
     Use it to find out if two circular sprites are touching.
    Parameters: 
    a. A sprite object with `centerX`, `centerY` and `radius` properties.
    b. A sprite object with `centerX`, `centerY` and `radius`.
    */
    },
    {
      key: 'hitTestCircle',
      value: function hitTestCircle (c1, c2) {
        var global = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];

        //Add collision properties
        if (!c1._bumpPropertiesAdded) this.addCollisionProperties(c1);
        if (!c2._bumpPropertiesAdded) this.addCollisionProperties(c2);

        var vx = undefined,
          vy = undefined,
          magnitude = undefined,
          combinedRadii = undefined,
          hit = undefined;

        //Calculate the vector between the circles鈥� center points
        if (global) {
          //Use global coordinates
          vx = c2.gx + c2.width / 2 - c2.xAnchorOffset - (c1.gx + c1.width / 2 - c1.xAnchorOffset);
          vy = c2.gy + c2.width / 2 - c2.yAnchorOffset - (c1.gy + c1.width / 2 - c1.yAnchorOffset);
        } else {
          //Use local coordinates
          vx = c2.x + c2.width / 2 - c2.xAnchorOffset - (c1.x + c1.width / 2 - c1.xAnchorOffset);
          vy = c2.y + c2.width / 2 - c2.yAnchorOffset - (c1.y + c1.width / 2 - c1.yAnchorOffset);
        }

        //Find the distance between the circles by calculating
        //the vector's magnitude (how long the vector is)
        magnitude = Math.sqrt(vx * vx + vy * vy);

        //Add together the circles' total radii
        combinedRadii = c1.radius + c2.radius;

        //Set `hit` to `true` if the distance between the circles is
        //less than their `combinedRadii`
        hit = magnitude < combinedRadii;

        //`hit` will be either `true` or `false`
        return hit;
      }

      /*
    circleCollision
    ---------------
     Use it to prevent a moving circular sprite from overlapping and optionally
    bouncing off a non-moving circular sprite.
    Parameters: 
    a. A sprite object with `x`, `y` `centerX`, `centerY` and `radius` properties.
    b. A sprite object with `x`, `y` `centerX`, `centerY` and `radius` properties.
    c. Optional: true or false to indicate whether or not the first sprite
    should bounce off the second sprite.
    The sprites can contain an optional mass property that should be greater than 1.
     */
    },
    {
      key: 'circleCollision',
      value: function circleCollision (c1, c2) {
        var bounce = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
        var global = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];

        //Add collision properties
        if (!c1._bumpPropertiesAdded) this.addCollisionProperties(c1);
        if (!c2._bumpPropertiesAdded) this.addCollisionProperties(c2);

        var magnitude = undefined,
          combinedRadii = undefined,
          overlap = undefined,
          vx = undefined,
          vy = undefined,
          dx = undefined,
          dy = undefined,
          s = {},
          hit = false;

        //Calculate the vector between the circles鈥� center points

        if (global) {
          //Use global coordinates
          vx = c2.gx + c2.width / 2 - c2.xAnchorOffset - (c1.gx + c1.width / 2 - c1.xAnchorOffset);
          vy = c2.gy + c2.width / 2 - c2.yAnchorOffset - (c1.gy + c1.width / 2 - c1.yAnchorOffset);
        } else {
          //Use local coordinates
          vx = c2.x + c2.width / 2 - c2.xAnchorOffset - (c1.x + c1.width / 2 - c1.xAnchorOffset);
          vy = c2.y + c2.width / 2 - c2.yAnchorOffset - (c1.y + c1.width / 2 - c1.yAnchorOffset);
        }

        //Find the distance between the circles by calculating
        //the vector's magnitude (how long the vector is)
        magnitude = Math.sqrt(vx * vx + vy * vy);

        //Add together the circles' combined half-widths
        combinedRadii = c1.radius + c2.radius;

        //Figure out if there's a collision
        if (magnitude < combinedRadii) {
          //Yes, a collision is happening
          hit = true;

          //Find the amount of overlap between the circles
          overlap = combinedRadii - magnitude;

          //Add some "quantum padding". This adds a tiny amount of space
          //between the circles to reduce their surface tension and make
          //them more slippery. "0.3" is a good place to start but you might
          //need to modify this slightly depending on the exact behaviour
          //you want. Too little and the balls will feel sticky, too much
          //and they could start to jitter if they're jammed together
          var quantumPadding = 0.3;
          overlap += quantumPadding;

          //Normalize the vector
          //These numbers tell us the direction of the collision
          dx = vx / magnitude;
          dy = vy / magnitude;

          //Move circle 1 out of the collision by multiplying
          //the overlap with the normalized vector and subtract it from
          //circle 1's position
          c1.x -= overlap * dx;
          c1.y -= overlap * dy;

          //Bounce
          if (bounce) {
            //Create a collision vector object, `s` to represent the bounce "surface".
            //Find the bounce surface's x and y properties
            //(This represents the normal of the distance vector between the circles)
            s.x = vy;
            s.y = -vx;

            //Bounce c1 off the surface
            this.bounceOffSurface(c1, s);
          }
        }
        return hit;
      }

      /*
    movingCircleCollision
    ---------------------
     Use it to make two moving circles bounce off each other.
    Parameters: 
    a. A sprite object with `x`, `y` `centerX`, `centerY` and `radius` properties.
    b. A sprite object with `x`, `y` `centerX`, `centerY` and `radius` properties.
    The sprites can contain an optional mass property that should be greater than 1.
     */
    },
    {
      key: 'movingCircleCollision',
      value: function movingCircleCollision (c1, c2) {
        var global = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];

        //Add collision properties
        if (!c1._bumpPropertiesAdded) this.addCollisionProperties(c1);
        if (!c2._bumpPropertiesAdded) this.addCollisionProperties(c2);

        var combinedRadii = undefined,
          overlap = undefined,
          xSide = undefined,
          ySide = undefined,
          //`s` refers to the distance vector between the circles
          s = {},
          p1A = {},
          p1B = {},
          p2A = {},
          p2B = {},
          hit = false;

        //Apply mass, if the circles have mass properties
        c1.mass = c1.mass || 1;
        c2.mass = c2.mass || 1;

        //Calculate the vector between the circles鈥� center points
        if (global) {
          //Use global coordinates
          s.vx = c2.gx + c2.radius - c2.xAnchorOffset - (c1.gx + c1.radius - c1.xAnchorOffset);
          s.vy = c2.gy + c2.radius - c2.yAnchorOffset - (c1.gy + c1.radius - c1.yAnchorOffset);
        } else {
          //Use local coordinates
          s.vx = c2.x + c2.radius - c2.xAnchorOffset - (c1.x + c1.radius - c1.xAnchorOffset);
          s.vy = c2.y + c2.radius - c2.yAnchorOffset - (c1.y + c1.radius - c1.yAnchorOffset);
        }

        //Find the distance between the circles by calculating
        //the vector's magnitude (how long the vector is)
        s.magnitude = Math.sqrt(s.vx * s.vx + s.vy * s.vy);

        //Add together the circles' combined half-widths
        combinedRadii = c1.radius + c2.radius;

        //Figure out if there's a collision
        if (s.magnitude < combinedRadii) {
          //Yes, a collision is happening
          hit = true;

          //Find the amount of overlap between the circles
          overlap = combinedRadii - s.magnitude;

          //Add some "quantum padding" to the overlap
          overlap += 0.3;

          //Normalize the vector.
          //These numbers tell us the direction of the collision
          s.dx = s.vx / s.magnitude;
          s.dy = s.vy / s.magnitude;

          //Find the collision vector.
          //Divide it in half to share between the circles, and make it absolute
          s.vxHalf = Math.abs(s.dx * overlap / 2);
          s.vyHalf = Math.abs(s.dy * overlap / 2);

          //Find the side that the collision is occurring on
          c1.x > c2.x ? (xSide = 1) : (xSide = -1);
          c1.y > c2.y ? (ySide = 1) : (ySide = -1);

          //Move c1 out of the collision by multiplying
          //the overlap with the normalized vector and adding it to
          //the circles' positions
          c1.x = c1.x + s.vxHalf * xSide;
          c1.y = c1.y + s.vyHalf * ySide;

          //Move c2 out of the collision
          c2.x = c2.x + s.vxHalf * -xSide;
          c2.y = c2.y + s.vyHalf * -ySide;

          //1. Calculate the collision surface's properties

          //Find the surface vector's left normal
          s.lx = s.vy;
          s.ly = -s.vx;

          //2. Bounce c1 off the surface (s)

          //Find the dot product between c1 and the surface
          var dp1 = c1.vx * s.dx + c1.vy * s.dy;

          //Project c1's velocity onto the collision surface
          p1A.x = dp1 * s.dx;
          p1A.y = dp1 * s.dy;

          //Find the dot product of c1 and the surface's left normal (s.lx and s.ly)
          var dp2 = c1.vx * (s.lx / s.magnitude) + c1.vy * (s.ly / s.magnitude);

          //Project the c1's velocity onto the surface's left normal
          p1B.x = dp2 * (s.lx / s.magnitude);
          p1B.y = dp2 * (s.ly / s.magnitude);

          //3. Bounce c2 off the surface (s)

          //Find the dot product between c2 and the surface
          var dp3 = c2.vx * s.dx + c2.vy * s.dy;

          //Project c2's velocity onto the collision surface
          p2A.x = dp3 * s.dx;
          p2A.y = dp3 * s.dy;

          //Find the dot product of c2 and the surface's left normal (s.lx and s.ly)
          var dp4 = c2.vx * (s.lx / s.magnitude) + c2.vy * (s.ly / s.magnitude);

          //Project c2's velocity onto the surface's left normal
          p2B.x = dp4 * (s.lx / s.magnitude);
          p2B.y = dp4 * (s.ly / s.magnitude);

          //4. Calculate the bounce vectors

          //Bounce c1
          //using p1B and p2A
          c1.bounce = {};
          c1.bounce.x = p1B.x + p2A.x;
          c1.bounce.y = p1B.y + p2A.y;

          //Bounce c2
          //using p1A and p2B
          c2.bounce = {};
          c2.bounce.x = p1A.x + p2B.x;
          c2.bounce.y = p1A.y + p2B.y;

          //Add the bounce vector to the circles' velocity
          //and add mass if the circle has a mass property
          c1.vx = c1.bounce.x / c1.mass;
          c1.vy = c1.bounce.y / c1.mass;
          c2.vx = c2.bounce.x / c2.mass;
          c2.vy = c2.bounce.y / c2.mass;
        }
        return hit;
      }
      /*
    multipleCircleCollision
    -----------------------
     Checks all the circles in an array for a collision against
    all the other circles in an array, using `movingCircleCollision` (above)
    */
    },
    {
      key: 'multipleCircleCollision',
      value: function multipleCircleCollision (arrayOfCircles) {
        var global = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];

        for (var i = 0; i < arrayOfCircles.length; i++) {
          //The first circle to use in the collision check
          var c1 = arrayOfCircles[i];
          for (var j = i + 1; j < arrayOfCircles.length; j++) {
            //The second circle to use in the collision check
            var c2 = arrayOfCircles[j];

            //Check for a collision and bounce the circles apart if
            //they collide. Use an optional `mass` property on the sprite
            //to affect the bounciness of each marble
            this.movingCircleCollision(c1, c2, global);
          }
        }
      }

      /*
    rectangleCollision
    ------------------
     Use it to prevent two rectangular sprites from overlapping.
    Optionally, make the first rectangle bounce off the second rectangle.
    Parameters:
    a. A sprite object with `x`, `y` `centerX`, `centerY`, `halfWidth` and `halfHeight` properties.
    b. A sprite object with `x`, `y` `centerX`, `centerY`, `halfWidth` and `halfHeight` properties.
    c. Optional: true or false to indicate whether or not the first sprite
    should bounce off the second sprite.
    */
    },
    {
      key: 'rectangleCollision',
      value: function rectangleCollision (r1, r2) {
        var bounce = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
        var global = arguments.length <= 3 || arguments[3] === undefined ? true : arguments[3];

        //Add collision properties
        if (!r1._bumpPropertiesAdded) this.addCollisionProperties(r1);
        if (!r2._bumpPropertiesAdded) this.addCollisionProperties(r2);

        var collision = undefined,
          combinedHalfWidths = undefined,
          combinedHalfHeights = undefined,
          overlapX = undefined,
          overlapY = undefined,
          vx = undefined,
          vy = undefined;

        //Calculate the distance vector
        if (global) {
          vx = r1.gx + Math.abs(r1.halfWidth) - r1.xAnchorOffset - (r2.gx + Math.abs(r2.halfWidth) - r2.xAnchorOffset);
          vy =
            r1.gy + Math.abs(r1.halfHeight) - r1.yAnchorOffset - (r2.gy + Math.abs(r2.halfHeight) - r2.yAnchorOffset);
        } else {
          //vx = r1.centerX - r2.centerX;
          //vy = r1.centerY - r2.centerY;
          vx = r1.x + Math.abs(r1.halfWidth) - r1.xAnchorOffset - (r2.x + Math.abs(r2.halfWidth) - r2.xAnchorOffset);
          vy = r1.y + Math.abs(r1.halfHeight) - r1.yAnchorOffset - (r2.y + Math.abs(r2.halfHeight) - r2.yAnchorOffset);
        }

        //Figure out the combined half-widths and half-heights
        combinedHalfWidths = Math.abs(r1.halfWidth) + Math.abs(r2.halfWidth);
        combinedHalfHeights = Math.abs(r1.halfHeight) + Math.abs(r2.halfHeight);

        //Check whether vx is less than the combined half widths
        if (Math.abs(vx) < combinedHalfWidths) {
          //A collision might be occurring!
          //Check whether vy is less than the combined half heights
          if (Math.abs(vy) < combinedHalfHeights) {
            //A collision has occurred! This is good!
            //Find out the size of the overlap on both the X and Y axes
            overlapX = combinedHalfWidths - Math.abs(vx);
            overlapY = combinedHalfHeights - Math.abs(vy);

            //The collision has occurred on the axis with the
            //*smallest* amount of overlap. Let's figure out which
            //axis that is

            if (overlapX >= overlapY) {
              //The collision is happening on the X axis
              //But on which side? vy can tell us

              if (vy > 0) {
                collision = 'top';
                //Move the rectangle out of the collision
                r1.y = r1.y + overlapY;
              } else {
                collision = 'bottom';
                //Move the rectangle out of the collision
                r1.y = r1.y - overlapY;
              }

              //Bounce
              if (bounce) {
                r1.vy *= -1;

                /*Alternative
              //Find the bounce surface's vx and vy properties
              var s = {};
              s.vx = r2.x - r2.x + r2.width;
              s.vy = 0;
               //Bounce r1 off the surface
              //this.bounceOffSurface(r1, s);
              */
              }
            } else {
              //The collision is happening on the Y axis
              //But on which side? vx can tell us

              if (vx > 0) {
                collision = 'left';
                //Move the rectangle out of the collision
                r1.x = r1.x + overlapX;
              } else {
                collision = 'right';
                //Move the rectangle out of the collision
                r1.x = r1.x - overlapX;
              }

              //Bounce
              if (bounce) {
                r1.vx *= -1;

                /*Alternative
                //Find the bounce surface's vx and vy properties
                var s = {};
                s.vx = 0;
                s.vy = r2.y - r2.y + r2.height;
                 //Bounce r1 off the surface
                this.bounceOffSurface(r1, s);
                */
              }
            }
          } else {
            //No collision
          }
        } else {
        }
        //No collision

        //Return the collision string. it will be either "top", "right",
        //"bottom", or "left" depending on which side of r1 is touching r2.
        return collision;
      }

      /*
    hitTestRectangle
    ----------------
     Use it to find out if two rectangular sprites are touching.
    Parameters: 
    a. A sprite object with `centerX`, `centerY`, `halfWidth` and `halfHeight` properties.
    b. A sprite object with `centerX`, `centerY`, `halfWidth` and `halfHeight` properties.
     */
    },
    {
      key: 'hitTestRectangle',
      value: function hitTestRectangle (r1, r2) {
        var global = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];

        //Add collision properties
        if (!r1._bumpPropertiesAdded) this.addCollisionProperties(r1);
        if (!r2._bumpPropertiesAdded) this.addCollisionProperties(r2);

        var hit = undefined,
          combinedHalfWidths = undefined,
          combinedHalfHeights = undefined,
          vx = undefined,
          vy = undefined;

        //A variable to determine whether there's a collision
        hit = false;

        //Calculate the distance vector
        if (global) {
          vx = r1.gx + Math.abs(r1.halfWidth) - r1.xAnchorOffset - (r2.gx + Math.abs(r2.halfWidth) - r2.xAnchorOffset);
          vy =
            r1.gy + Math.abs(r1.halfHeight) - r1.yAnchorOffset - (r2.gy + Math.abs(r2.halfHeight) - r2.yAnchorOffset);
        } else {
          vx = r1.x + Math.abs(r1.halfWidth) - r1.xAnchorOffset - (r2.x + Math.abs(r2.halfWidth) - r2.xAnchorOffset);
          vy = r1.y + Math.abs(r1.halfHeight) - r1.yAnchorOffset - (r2.y + Math.abs(r2.halfHeight) - r2.yAnchorOffset);
        }

        //Figure out the combined half-widths and half-heights
        combinedHalfWidths = Math.abs(r1.halfWidth) + Math.abs(r2.halfWidth);
        combinedHalfHeights = Math.abs(r1.halfHeight) + Math.abs(r2.halfHeight);

        //Check for a collision on the x axis
        if (Math.abs(vx) < combinedHalfWidths) {
          //A collision might be occuring. Check for a collision on the y axis
          if (Math.abs(vy) < combinedHalfHeights) {
            //There's definitely a collision happening
            hit = true;
          } else {
            //There's no collision on the y axis
            hit = false;
          }
        } else {
          //There's no collision on the x axis
          hit = false;
        }

        //`hit` will be either `true` or `false`
        return hit;
      }

      /*
    hitTestCircleRectangle
    ----------------
     Use it to find out if a circular shape is touching a rectangular shape
    Parameters: 
    a. A sprite object with `centerX`, `centerY`, `halfWidth` and `halfHeight` properties.
    b. A sprite object with `centerX`, `centerY`, `halfWidth` and `halfHeight` properties.
     */
    },
    {
      key: 'hitTestCircleRectangle',
      value: function hitTestCircleRectangle (c1, r1) {
        var global = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];

        //Add collision properties
        if (!r1._bumpPropertiesAdded) this.addCollisionProperties(r1);
        if (!c1._bumpPropertiesAdded) this.addCollisionProperties(c1);

        var region = undefined,
          collision = undefined,
          c1x = undefined,
          c1y = undefined,
          r1x = undefined,
          r1y = undefined;

        //Use either global or local coordinates
        if (global) {
          c1x = c1.gx;
          c1y = c1.gy;
          r1x = r1.gx;
          r1y = r1.gy;
        } else {
          c1x = c1.x;
          c1y = c1.y;
          r1x = r1.x;
          r1y = r1.y;
        }

        //Is the circle above the rectangle's top edge?
        if (c1y - c1.yAnchorOffset < r1y - Math.abs(r1.halfHeight) - r1.yAnchorOffset) {
          //If it is, we need to check whether it's in the
          //top left, top center or top right
          if (c1x - c1.xAnchorOffset < r1x - 1 - Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'topLeft';
          } else if (c1x - c1.xAnchorOffset > r1x + 1 + Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'topRight';
          } else {
            region = 'topMiddle';
          }
        } else if (c1y - c1.yAnchorOffset > r1y + Math.abs(r1.halfHeight) - r1.yAnchorOffset) {
          //The circle isn't above the top edge, so it might be
          //below the bottom edge
          //If it is, we need to check whether it's in the bottom left,
          //bottom center, or bottom right
          if (c1x - c1.xAnchorOffset < r1x - 1 - Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'bottomLeft';
          } else if (c1x - c1.xAnchorOffset > r1x + 1 + Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'bottomRight';
          } else {
            region = 'bottomMiddle';
          }
        } else {
          //The circle isn't above the top edge or below the bottom edge,
          //so it must be on the left or right side
          if (c1x - c1.xAnchorOffset < r1x - Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'leftMiddle';
          } else {
            region = 'rightMiddle';
          }
        }

        //Is this the circle touching the flat sides
        //of the rectangle?
        if (
          region === 'topMiddle' ||
          region === 'bottomMiddle' ||
          region === 'leftMiddle' ||
          region === 'rightMiddle'
        ) {
          //Yes, it is, so do a standard rectangle vs. rectangle collision test
          collision = this.hitTestRectangle(c1, r1, global);
        } else {
          //The circle is touching one of the corners, so do a
          //circle vs. point collision test
          var point = {};

          switch (region) {
            case 'topLeft':
              point.x = r1x - r1.xAnchorOffset;
              point.y = r1y - r1.yAnchorOffset;
              break;

            case 'topRight':
              point.x = r1x + r1.width - r1.xAnchorOffset;
              point.y = r1y - r1.yAnchorOffset;
              break;

            case 'bottomLeft':
              point.x = r1x - r1.xAnchorOffset;
              point.y = r1y + r1.height - r1.yAnchorOffset;
              break;

            case 'bottomRight':
              point.x = r1x + r1.width - r1.xAnchorOffset;
              point.y = r1y + r1.height - r1.yAnchorOffset;
          }

          //Check for a collision between the circle and the point
          collision = this.hitTestCirclePoint(c1, point, global);
        }

        //Return the result of the collision.
        //The return value will be `undefined` if there's no collision
        if (collision) {
          return region;
        } else {
          return collision;
        }
      }

      /*
    hitTestCirclePoint
    ------------------
     Use it to find out if a circular shape is touching a point
    Parameters: 
    a. A sprite object with `centerX`, `centerY`, and `radius` properties.
    b. A point object with `x` and `y` properties.
     */
    },
    {
      key: 'hitTestCirclePoint',
      value: function hitTestCirclePoint (c1, point) {
        var global = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];

        //Add collision properties
        if (!c1._bumpPropertiesAdded) this.addCollisionProperties(c1);

        //A point is just a circle with a diameter of
        //1 pixel, so we can cheat. All we need to do is an ordinary circle vs. circle
        //Collision test. Just supply the point with the properties
        //it needs
        point.diameter = 1;
        point.width = point.diameter;
        point.radius = 0.5;
        point.centerX = point.x;
        point.centerY = point.y;
        point.gx = point.x;
        point.gy = point.y;
        point.xAnchorOffset = 0;
        point.yAnchorOffset = 0;
        point._bumpPropertiesAdded = true;
        return this.hitTestCircle(c1, point, global);
      }

      /*
    circleRectangleCollision
    ------------------------
     Use it to bounce a circular shape off a rectangular shape
    Parameters: 
    a. A sprite object with `centerX`, `centerY`, `halfWidth` and `halfHeight` properties.
    b. A sprite object with `centerX`, `centerY`, `halfWidth` and `halfHeight` properties.
     */
    },
    {
      key: 'circleRectangleCollision',
      value: function circleRectangleCollision (c1, r1) {
        var bounce = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
        var global = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];

        //Add collision properties
        if (!r1._bumpPropertiesAdded) this.addCollisionProperties(r1);
        if (!c1._bumpPropertiesAdded) this.addCollisionProperties(c1);

        var region = undefined,
          collision = undefined,
          c1x = undefined,
          c1y = undefined,
          r1x = undefined,
          r1y = undefined;

        //Use either the global or local coordinates
        if (global) {
          c1x = c1.gx;
          c1y = c1.gy;
          r1x = r1.gx;
          r1y = r1.gy;
        } else {
          c1x = c1.x;
          c1y = c1.y;
          r1x = r1.x;
          r1y = r1.y;
        }

        //Is the circle above the rectangle's top edge?
        if (c1y - c1.yAnchorOffset < r1y - Math.abs(r1.halfHeight) - r1.yAnchorOffset) {
          //If it is, we need to check whether it's in the
          //top left, top center or top right
          if (c1x - c1.xAnchorOffset < r1x - 1 - Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'topLeft';
          } else if (c1x - c1.xAnchorOffset > r1x + 1 + Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'topRight';
          } else {
            region = 'topMiddle';
          }
        } else if (c1y - c1.yAnchorOffset > r1y + Math.abs(r1.halfHeight) - r1.yAnchorOffset) {
          //The circle isn't above the top edge, so it might be
          //below the bottom edge
          //If it is, we need to check whether it's in the bottom left,
          //bottom center, or bottom right
          if (c1x - c1.xAnchorOffset < r1x - 1 - Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'bottomLeft';
          } else if (c1x - c1.xAnchorOffset > r1x + 1 + Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'bottomRight';
          } else {
            region = 'bottomMiddle';
          }
        } else {
          //The circle isn't above the top edge or below the bottom edge,
          //so it must be on the left or right side
          if (c1x - c1.xAnchorOffset < r1x - Math.abs(r1.halfWidth) - r1.xAnchorOffset) {
            region = 'leftMiddle';
          } else {
            region = 'rightMiddle';
          }
        }

        //Is this the circle touching the flat sides
        //of the rectangle?
        if (
          region === 'topMiddle' ||
          region === 'bottomMiddle' ||
          region === 'leftMiddle' ||
          region === 'rightMiddle'
        ) {
          //Yes, it is, so do a standard rectangle vs. rectangle collision test
          collision = this.rectangleCollision(c1, r1, bounce, global);
        } else {
          //The circle is touching one of the corners, so do a
          //circle vs. point collision test
          var point = {};

          switch (region) {
            case 'topLeft':
              point.x = r1x - r1.xAnchorOffset;
              point.y = r1y - r1.yAnchorOffset;
              break;

            case 'topRight':
              point.x = r1x + r1.width - r1.xAnchorOffset;
              point.y = r1y - r1.yAnchorOffset;
              break;

            case 'bottomLeft':
              point.x = r1x - r1.xAnchorOffset;
              point.y = r1y + r1.height - r1.yAnchorOffset;
              break;

            case 'bottomRight':
              point.x = r1x + r1.width - r1.xAnchorOffset;
              point.y = r1y + r1.height - r1.yAnchorOffset;
          }

          //Check for a collision between the circle and the point
          collision = this.circlePointCollision(c1, point, bounce, global);
        }

        if (collision) {
          return region;
        } else {
          return collision;
        }
      }

      /*
    circlePointCollision
    --------------------
     Use it to boucnce a circle off a point.
    Parameters: 
    a. A sprite object with `centerX`, `centerY`, and `radius` properties.
    b. A point object with `x` and `y` properties.
     */
    },
    {
      key: 'circlePointCollision',
      value: function circlePointCollision (c1, point) {
        var bounce = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
        var global = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];

        //Add collision properties
        if (!c1._bumpPropertiesAdded) this.addCollisionProperties(c1);

        //A point is just a circle with a diameter of
        //1 pixel, so we can cheat. All we need to do is an ordinary circle vs. circle
        //Collision test. Just supply the point with the properties
        //it needs
        point.diameter = 1;
        point.width = point.diameter;
        point.radius = 0.5;
        point.centerX = point.x;
        point.centerY = point.y;
        point.gx = point.x;
        point.gy = point.y;
        point.xAnchorOffset = 0;
        point.yAnchorOffset = 0;
        point._bumpPropertiesAdded = true;
        return this.circleCollision(c1, point, bounce, global);
      }

      /*
    bounceOffSurface
    ----------------
     Use this to bounce an object off another object.
    Parameters: 
    a. An object with `v.x` and `v.y` properties. This represents the object that is colliding
    with a surface.
    b. An object with `x` and `y` properties. This represents the surface that the object
    is colliding into.
    The first object can optionally have a mass property that's greater than 1. The mass will
    be used to dampen the bounce effect.
    */
    },
    {
      key: 'bounceOffSurface',
      value: function bounceOffSurface (o, s) {
        //Add collision properties
        if (!o._bumpPropertiesAdded) this.addCollisionProperties(o);

        var dp1 = undefined,
          dp2 = undefined,
          p1 = {},
          p2 = {},
          bounce = {},
          mass = o.mass || 1;

        //1. Calculate the collision surface's properties
        //Find the surface vector's left normal
        s.lx = s.y;
        s.ly = -s.x;

        //Find its magnitude
        s.magnitude = Math.sqrt(s.x * s.x + s.y * s.y);

        //Find its normalized values
        s.dx = s.x / s.magnitude;
        s.dy = s.y / s.magnitude;

        //2. Bounce the object (o) off the surface (s)

        //Find the dot product between the object and the surface
        dp1 = o.vx * s.dx + o.vy * s.dy;

        //Project the object's velocity onto the collision surface
        p1.vx = dp1 * s.dx;
        p1.vy = dp1 * s.dy;

        //Find the dot product of the object and the surface's left normal (s.lx and s.ly)
        dp2 = o.vx * (s.lx / s.magnitude) + o.vy * (s.ly / s.magnitude);

        //Project the object's velocity onto the surface's left normal
        p2.vx = dp2 * (s.lx / s.magnitude);
        p2.vy = dp2 * (s.ly / s.magnitude);

        //Reverse the projection on the surface's left normal
        p2.vx *= -1;
        p2.vy *= -1;

        //Add up the projections to create a new bounce vector
        bounce.x = p1.vx + p2.vx;
        bounce.y = p1.vy + p2.vy;

        //Assign the bounce vector to the object's velocity
        //with optional mass to dampen the effect
        o.vx = bounce.x / mass;
        o.vy = bounce.y / mass;
      }

      /*
    contain
    -------
    `contain` can be used to contain a sprite with `x` and
    `y` properties inside a rectangular area.
     The `contain` function takes four arguments: a sprite with `x` and `y`
    properties, an object literal with `x`, `y`, `width` and `height` properties. The 
    third argument is a Boolean (true/false) value that determines if the sprite
    should bounce when it hits the edge of the container. The fourth argument
    is an extra user-defined callback function that you can call when the
    sprite hits the container
    ```js
    contain(anySprite, {x: 0, y: 0, width: 512, height: 512}, true, callbackFunction);
    ```
    The code above will contain the sprite's position inside the 512 by
    512 pixel area defined by the object. If the sprite hits the edges of
    the container, it will bounce. The `callBackFunction` will run if 
    there's a collision.
     An additional feature of the `contain` method is that if the sprite
    has a `mass` property, it will be used to dampen the sprite's bounce
    in a natural looking way.
     If the sprite bumps into any of the containing object's boundaries,
    the `contain` function will return a value that tells you which side
    the sprite bumped into: 鈥渓eft鈥�, 鈥渢op鈥�, 鈥渞ight鈥� or 鈥渂ottom鈥�. Here's how
    you could keep the sprite contained and also find out which boundary
    it hit:
    ```js
    //Contain the sprite and find the collision value
    let collision = contain(anySprite, {x: 0, y: 0, width: 512, height: 512});
     //If there's a collision, display the boundary that the collision happened on
    if(collision) {
      if collision.has("left") console.log("The sprite hit the left");  
      if collision.has("top") console.log("The sprite hit the top");  
      if collision.has("right") console.log("The sprite hit the right");  
      if collision.has("bottom") console.log("The sprite hit the bottom");  
    }
    ```
    If the sprite doesn't hit a boundary, the value of
    `collision` will be `undefined`. 
    */

      /*
     contain(sprite, container, bounce = false, extra = undefined) {
        //Helper methods that compensate for any possible shift the the
       //sprites' anchor points
       let nudgeAnchor = (o, value, axis) => {
         if (o.anchor !== undefined) {
           if (o.anchor[axis] !== 0) {
             return value * ((1 - o.anchor[axis]) - o.anchor[axis]);
           } else {
             return value;
           }
         } else {
           return value; 
         }
       };
        let compensateForAnchor = (o, value, axis) => {
         if (o.anchor !== undefined) {
           if (o.anchor[axis] !== 0) {
             return value * o.anchor[axis];
           } else {
             return 0;
           }
         } else {
           return 0; 
         }
       };
        let compensateForAnchors = (a, b, property1, property2) => {
          return compensateForAnchor(a, a[property1], property2) + compensateForAnchor(b, b[property1], property2)
       };    
       //Create a set called `collision` to keep track of the
       //boundaries with which the sprite is colliding
       let collision = new Set();
        //Left
       if (sprite.x - compensateForAnchor(sprite, sprite.width, "x") < container.x - sprite.parent.gx - compensateForAnchor(container, container.width, "x")) {
         //Bounce the sprite if `bounce` is true
         if (bounce) sprite.vx *= -1;
          //If the sprite has `mass`, let the mass
         //affect the sprite's velocity
         if(sprite.mass) sprite.vx /= sprite.mass;
          //Keep the sprite inside the container
         sprite.x = container.x - sprite.parent.gx + compensateForAnchor(sprite, sprite.width, "x") - compensateForAnchor(container, container.width, "x");
          //Add "left" to the collision set
         collision.add("left");
       }
        //Top
       if (sprite.y - compensateForAnchor(sprite, sprite.height, "y") < container.y - sprite.parent.gy - compensateForAnchor(container, container.height, "y")) {
         if (bounce) sprite.vy *= -1;
         if(sprite.mass) sprite.vy /= sprite.mass;
         sprite.y = container.x - sprite.parent.gy + compensateForAnchor(sprite, sprite.height, "y") - compensateForAnchor(container, container.height, "y");
         collision.add("top");
       }
        //Right
       if (sprite.x - compensateForAnchor(sprite, sprite.width, "x") + sprite.width > container.width - compensateForAnchor(container, container.width, "x")) {
         if (bounce) sprite.vx *= -1;
         if(sprite.mass) sprite.vx /= sprite.mass;
         sprite.x = container.width - sprite.width + compensateForAnchor(sprite, sprite.width, "x") - compensateForAnchor(container, container.width, "x");
         collision.add("right");
       }
        //Bottom
       if (sprite.y - compensateForAnchor(sprite, sprite.height, "y") + sprite.height > container.height - compensateForAnchor(container, container.height, "y")) {
         if (bounce) sprite.vy *= -1;
         if(sprite.mass) sprite.vy /= sprite.mass;
         sprite.y = container.height - sprite.height + compensateForAnchor(sprite, sprite.height, "y") - compensateForAnchor(container, container.height, "y");
         collision.add("bottom");
       }
        //If there were no collisions, set `collision` to `undefined`
       if (collision.size === 0) collision = undefined;
        //The `extra` function runs if there was a collision
       //and `extra` has been defined
       if (collision && extra) extra(collision);
        //Return the `collision` value
       return collision;
     }
     */
    },
    {
      key: 'contain',
      value: function contain (sprite, container) {
        var bounce = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
        var extra = arguments.length <= 3 || arguments[3] === undefined ? undefined : arguments[3];

        //Add collision properties
        if (!sprite._bumpPropertiesAdded) this.addCollisionProperties(sprite);

        //Give the container x and y anchor offset values, if it doesn't
        //have any
        if (container.xAnchorOffset === undefined) container.xAnchorOffset = 0;
        if (container.yAnchorOffset === undefined) container.yAnchorOffset = 0;
        if (sprite.parent.gx === undefined) sprite.parent.gx = 0;
        if (sprite.parent.gy === undefined) sprite.parent.gy = 0;

        //Create a Set called `collision` to keep track of the
        //boundaries with which the sprite is colliding
        var collision = new Set();

        //Left
        if (sprite.x - sprite.xAnchorOffset < container.x - sprite.parent.gx - container.xAnchorOffset) {
          //Bounce the sprite if `bounce` is true
          if (bounce) sprite.vx *= -1;

          //If the sprite has `mass`, let the mass
          //affect the sprite's velocity
          if (sprite.mass) sprite.vx /= sprite.mass;

          //Reposition the sprite inside the container
          sprite.x = container.x - sprite.parent.gx - container.xAnchorOffset + sprite.xAnchorOffset;

          //Make a record of the side which the container hit
          collision.add('left');
        }

        //Top
        if (sprite.y - sprite.yAnchorOffset < container.y - sprite.parent.gy - container.yAnchorOffset) {
          if (bounce) sprite.vy *= -1;
          if (sprite.mass) sprite.vy /= sprite.mass;
          sprite.y = container.y - sprite.parent.gy - container.yAnchorOffset + sprite.yAnchorOffset;
          collision.add('top');
        }

        //Right
        if (sprite.x - sprite.xAnchorOffset + sprite.width > container.width - container.xAnchorOffset) {
          if (bounce) sprite.vx *= -1;
          if (sprite.mass) sprite.vx /= sprite.mass;
          sprite.x = container.width - sprite.width - container.xAnchorOffset + sprite.xAnchorOffset;
          collision.add('right');
        }

        //Bottom
        if (sprite.y - sprite.yAnchorOffset + sprite.height > container.height - container.yAnchorOffset) {
          if (bounce) sprite.vy *= -1;
          if (sprite.mass) sprite.vy /= sprite.mass;
          sprite.y = container.height - sprite.height - container.yAnchorOffset + sprite.yAnchorOffset;
          collision.add('bottom');
        }

        //If there were no collisions, set `collision` to `undefined`
        if (collision.size === 0) collision = undefined;

        //The `extra` function runs if there was a collision
        //and `extra` has been defined
        if (collision && extra) extra(collision);

        //Return the `collision` value
        return collision;
      }

      //`outsideBounds` checks whether a sprite is outide the boundary of
      //another object. It returns an object called `collision`. `collision` will be `undefined` if there's no
      //collision. But if there is a collision, `collision` will be
      //returned as a Set containg strings that tell you which boundary
      //side was crossed: "left", "right", "top" or "bottom"
    },
    {
      key: 'outsideBounds',
      value: function outsideBounds (s, bounds, extra) {
        var x = bounds.x,
          y = bounds.y,
          width = bounds.width,
          height = bounds.height;

        //The `collision` object is used to store which
        //side of the containing rectangle the sprite hits
        var collision = new Set();

        //Left
        if (s.x < x - s.width) {
          collision.add('left');
        }
        //Top
        if (s.y < y - s.height) {
          collision.add('top');
        }
        //Right
        if (s.x > width + s.width) {
          collision.add('right');
        }
        //Bottom
        if (s.y > height + s.height) {
          collision.add('bottom');
        }

        //If there were no collisions, set `collision` to `undefined`
        if (collision.size === 0) collision = undefined;

        //The `extra` function runs if there was a collision
        //and `extra` has been defined
        if (collision && extra) extra(collision);

        //Return the `collision` object
        return collision;
      }

      /*
    _getCenter
    ----------
     A utility that finds the center point of the sprite. If it's anchor point is the
    sprite's top left corner, then the center is calculated from that point.
    If the anchor point has been shifted, then the anchor x/y point is used as the sprite's center
    */
    },
    {
      key: '_getCenter',
      value: function _getCenter (o, dimension, axis) {
        if (o.anchor !== undefined) {
          if (o.anchor[axis] !== 0) {
            return 0;
          } else {
            //console.log(o.anchor[axis])
            return dimension / 2;
          }
        } else {
          return dimension;
        }
      }

      /*
    hit
    ---
    A convenient universal collision function to test for collisions
    between rectangles, circles, and points.
    */
    },
    {
      key: 'hit',
      value: function hit (a, b) {
        var react = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
        var bounce = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
        var global = arguments[4];
        var extra = arguments.length <= 5 || arguments[5] === undefined ? undefined : arguments[5];

        //Local references to bump's collision methods
        var hitTestPoint = this.hitTestPoint.bind(this),
          hitTestRectangle = this.hitTestRectangle.bind(this),
          hitTestCircle = this.hitTestCircle.bind(this),
          movingCircleCollision = this.movingCircleCollision.bind(this),
          circleCollision = this.circleCollision.bind(this),
          hitTestCircleRectangle = this.hitTestCircleRectangle.bind(this),
          rectangleCollision = this.rectangleCollision.bind(this),
          circleRectangleCollision = this.circleRectangleCollision.bind(this);

        var collision = undefined,
          aIsASprite = a.parent !== undefined,
          bIsASprite = b.parent !== undefined;

        //Check to make sure one of the arguments isn't an array
        if ((aIsASprite && b instanceof Array) || (bIsASprite && a instanceof Array)) {
          //If it is, check for a collision between a sprite and an array
          spriteVsArray();
        } else {
          //If one of the arguments isn't an array, find out what type of
          //collision check to run
          collision = findCollisionType(a, b);
          if (collision && extra) extra(collision);
        }

        //Return the result of the collision.
        //It will be `undefined` if there's no collision and `true` if
        //there is a collision. `rectangleCollision` sets `collsision` to
        //"top", "bottom", "left" or "right" depeneding on which side the
        //collision is occuring on
        return collision;

        function findCollisionType (a, b) {
          //Are `a` and `b` both sprites?
          //(We have to check again if this function was called from
          //`spriteVsArray`)
          var aIsASprite = a.parent !== undefined;
          var bIsASprite = b.parent !== undefined;

          if (aIsASprite && bIsASprite) {
            //Yes, but what kind of sprites?
            if (a.diameter && b.diameter) {
              //They're circles
              return circleVsCircle(a, b);
            } else if (a.diameter && !b.diameter) {
              //The first one is a circle and the second is a rectangle
              return circleVsRectangle(a, b);
            } else {
              //They're rectangles
              return rectangleVsRectangle(a, b);
            }
          } else if (bIsASprite && !(a.x === undefined) && !(a.y === undefined)) {
            //They're not both sprites, so what are they?
            //Is `a` not a sprite and does it have x and y properties?
            //Yes, so this is a point vs. sprite collision test
            return hitTestPoint(a, b);
          } else {
            //The user is trying to test some incompatible objects
            throw new Error("I'm sorry, " + a + ' and ' + b + " cannot be use together in a collision test.'");
          }
        }

        function spriteVsArray () {
          //If `a` happens to be the array, flip it around so that it becomes `b`
          if (a instanceof Array) {
            var _ref = [_b, _a];
            var _a = _ref[0];
            var _b = _ref[1];
          }
          //Loop through the array in reverse
          for (var i = b.length - 1; i >= 0; i--) {
            var sprite = b[i];
            collision = findCollisionType(a, sprite);
            if (collision && extra) extra(collision, sprite);
          }
        }

        function circleVsCircle (a, b) {
          //If the circles shouldn't react to the collision,
          //just test to see if they're touching
          if (!react) {
            return hitTestCircle(a, b);
          } else {
            //Yes, the circles should react to the collision
            //Are they both moving?
            if (a.vx + a.vy !== 0 && b.vx + b.vy !== 0) {
              //Yes, they are both moving
              //(moving circle collisions always bounce apart so there's
              //no need for the third, `bounce`, argument)
              return movingCircleCollision(a, b, global);
            } else {
              //No, they're not both moving
              return circleCollision(a, b, bounce, global);
            }
          }
        }

        function rectangleVsRectangle (a, b) {
          //If the rectangles shouldn't react to the collision, just
          //test to see if they're touching
          if (!react) {
            return hitTestRectangle(a, b, global);
          } else {
            return rectangleCollision(a, b, bounce, global);
          }
        }

        function circleVsRectangle (a, b) {
          //If the rectangles shouldn't react to the collision, just
          //test to see if they're touching
          if (!react) {
            return hitTestCircleRectangle(a, b, global);
          } else {
            return circleRectangleCollision(a, b, bounce, global);
          }
        }
      }
    }
  ]);

  return Bump;
})();

//# sourceMappingURL=bump.js.map

var _createClass = (function () {
  function defineProperties (target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ('value' in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
})();

function _classCallCheck (instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError('Cannot call a class as a function');
  }
}

var Tink = (function () {
  function Tink (PIXI, element) {
    var scale = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];

    _classCallCheck(this, Tink);

    //Add element and scale properties
    this.element = element;
    this._scale = scale;

    //An array to store all the draggable sprites
    this.draggableSprites = [];

    //An array to store all the pointer objects
    //(there will usually just be one)
    this.pointers = [];

    //An array to store all the buttons and button-like
    //interactive sprites
    this.buttons = [];

    //A local PIXI reference
    this.PIXI = PIXI;

    //Aliases for Pixi objects
    this.TextureCache = this.PIXI.utils.TextureCache;

    //Note: change MovieClip to AnimatedSprite for Pixi v4
    this.AnimatedSprite = this.PIXI.AnimatedSprite;
    this.Texture = this.PIXI.Texture;
  }

  _createClass(Tink, [
    {
      key: 'makeDraggable',

      //`makeDraggable` lets you make a drag-and-drop sprite by pushing it
      //into the `draggableSprites` array
      value: function makeDraggable () {
        var _this = this;

        for (var _len = arguments.length, sprites = Array(_len), _key = 0; _key < _len; _key++) {
          sprites[_key] = arguments[_key];
        }

        //If the first argument isn't an array of sprites...
        if (!(sprites[0] instanceof Array)) {
          sprites.forEach(function (sprite) {
            _this.draggableSprites.push(sprite);

            //If the sprite's `draggable` property hasn't already been defined by
            //another library, like Hexi, define it
            if (sprite.draggable === undefined) {
              sprite.draggable = true;
              sprite._localDraggableAllocation = true;
            }
          });
        } else {
          //If the first argument is an array of sprites...
          var spritesArray = sprites[0];
          if (spritesArray.length > 0) {
            for (var i = spritesArray.length - 1; i >= 0; i--) {
              var sprite = spritesArray[i];
              this.draggableSprites.push(sprite);

              //If the sprite's `draggable` property hasn't already been defined by
              //another library, like Hexi, define it
              if (sprite.draggable === undefined) {
                sprite.draggable = true;
                sprite._localDraggableAllocation = true;
              }
            }
          }
        }
      }

      //`makeUndraggable` removes the sprite from the `draggableSprites`
      //array
    },
    {
      key: 'makeUndraggable',
      value: function makeUndraggable () {
        var _this2 = this;

        for (var _len2 = arguments.length, sprites = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
          sprites[_key2] = arguments[_key2];
        }

        //If the first argument isn't an array of sprites...
        if (!(sprites[0] instanceof Array)) {
          sprites.forEach(function (sprite) {
            _this2.draggableSprites.splice(_this2.draggableSprites.indexOf(sprite), 1);
            if (sprite._localDraggableAllocation === true) sprite.draggable = false;
          });
        } else {
          //If the first argument is an array of sprites
          var spritesArray = sprites[0];
          if (spritesArray.length > 0) {
            for (var i = spritesArray.length - 1; i >= 0; i--) {
              var sprite = spritesArray[i];
              this.draggableSprites.splice(this.draggableSprites.indexOf(sprite), 1);
              if (sprite._localDraggableAllocation === true) sprite.draggable = false;
            }
          }
        }
      }
    },
    {
      key: 'makePointer',
      value: function makePointer () {
        var element = arguments.length <= 0 || arguments[0] === undefined ? this.element : arguments[0];
        var scale = arguments.length <= 1 || arguments[1] === undefined ? this.scale : arguments[1];

        //Get a reference to Tink's global `draggableSprites` array
        var draggableSprites = this.draggableSprites;

        //Get a reference to Tink's `addGlobalPositionProperties` method
        var addGlobalPositionProperties = this.addGlobalPositionProperties;

        //The pointer object will be returned by this function
        var pointer = {
          element: element,
          _scale: scale,

          //Private x and y properties
          _x: 0,
          _y: 0,

          //Width and height
          width: 1,
          height: 1,

          //The public x and y properties are divided by the scale. If the
          //HTML element that the pointer is sensitive to (like the canvas)
          //is scaled up or down, you can change the `scale` value to
          //correct the pointer's position values
          get x () {
            return this._x / this.scale;
          },
          get y () {
            return this._y / this.scale;
          },

          //Add `centerX` and `centerY` getters so that we
          //can use the pointer's coordinates with easing
          //and collision functions
          get centerX () {
            return this.x;
          },
          get centerY () {
            return this.y;
          },

          //`position` returns an object with x and y properties that
          //contain the pointer's position
          get position () {
            return {
              x: this.x,
              y: this.y
            };
          },

          get scale () {
            return this._scale;
          },
          set scale (value) {
            this._scale = value;
          },

          //Add a `cursor` getter/setter to change the pointer's cursor
          //style. Values can be "pointer" (for a hand icon) or "auto" for
          //an ordinary arrow icon.
          get cursor () {
            return this.element.style.cursor;
          },
          set cursor (value) {
            this.element.style.cursor = value;
          },

          //Booleans to track the pointer state
          isDown: false,
          isUp: true,
          tapped: false,

          //Properties to help measure the time between up and down states
          downTime: 0,
          elapsedTime: 0,

          //Optional `press`,`release` and `tap` methods
          press: undefined,
          release: undefined,
          tap: undefined,

          //A `dragSprite` property to help with drag and drop
          dragSprite: null,

          //The drag offsets to help drag sprites
          dragOffsetX: 0,
          dragOffsetY: 0,

          //A property to check whether or not the pointer
          //is visible
          _visible: true,
          get visible () {
            return this._visible;
          },
          set visible (value) {
            if (value === true) {
              this.cursor = 'auto';
            } else {
              this.cursor = 'none';
            }
            this._visible = value;
          },

          //The pointer's mouse `moveHandler`
          moveHandler: function moveHandler (event) {
            //Get the element that's firing the event
            var element = event.target;

            //Find the pointer芒鈧劉s x and y position (for mouse).
            //Subtract the element's top and left offset from the browser window
            this._x = event.pageX - element.offsetLeft;
            this._y = event.pageY - element.offsetTop;

            //Prevent the event's default behavior
            event.preventDefault();
          },

          //The pointer's `touchmoveHandler`
          touchmoveHandler: function touchmoveHandler (event) {
            var element = event.target;

            //Find the touch point's x and y position
            this._x = event.targetTouches[0].pageX - element.offsetLeft;
            this._y = event.targetTouches[0].pageY - element.offsetTop;
            event.preventDefault();
          },

          //The pointer's `downHandler`
          downHandler: function downHandler (event) {
            //Set the down states
            this.isDown = true;
            this.isUp = false;
            this.tapped = false;

            //Capture the current time
            this.downTime = Date.now();

            //Call the `press` method if it's been assigned
            if (this.press) this.press();
            event.preventDefault();
          },

          //The pointer's `touchstartHandler`
          touchstartHandler: function touchstartHandler (event) {
            var element = event.target;

            //Find the touch point's x and y position
            this._x = event.targetTouches[0].pageX - element.offsetLeft;
            this._y = event.targetTouches[0].pageY - element.offsetTop;

            //Set the down states
            this.isDown = true;
            this.isUp = false;
            this.tapped = false;

            //Capture the current time
            this.downTime = Date.now();

            //Call the `press` method if it's been assigned
            if (this.press) this.press();
            event.preventDefault();
          },

          //The pointer's `upHandler`
          upHandler: function upHandler (event) {
            //Figure out how much time the pointer has been down
            this.elapsedTime = Math.abs(this.downTime - Date.now());

            //If it's less than 200 milliseconds, it must be a tap or click
            if (this.elapsedTime <= 200 && this.tapped === false) {
              this.tapped = true;

              //Call the `tap` method if it's been assigned
              if (this.tap) this.tap();
            }
            this.isUp = true;
            this.isDown = false;

            //Call the `release` method if it's been assigned
            if (this.release) this.release();

            //`event.preventDefault();` needs to be disabled to prevent <input> range sliders
            //from getting trapped in Firefox (and possibly Safari)
            //event.preventDefault();
          },

          //The pointer's `touchendHandler`
          touchendHandler: function touchendHandler (event) {
            //Figure out how much time the pointer has been down
            this.elapsedTime = Math.abs(this.downTime - Date.now());

            //If it's less than 200 milliseconds, it must be a tap or click
            if (this.elapsedTime <= 200 && this.tapped === false) {
              this.tapped = true;

              //Call the `tap` method if it's been assigned
              if (this.tap) this.tap();
            }
            this.isUp = true;
            this.isDown = false;

            //Call the `release` method if it's been assigned
            if (this.release) this.release();

            //event.preventDefault();
          },

          //`hitTestSprite` figures out if the pointer is touching a sprite
          hitTestSprite: function hitTestSprite (sprite) {
            //Add global `gx` and `gy` properties to the sprite if they
            //don't already exist
            addGlobalPositionProperties(sprite);

            //The `hit` variable will become `true` if the pointer is
            //touching the sprite and remain `false` if it isn't
            var hit = false;

            //Find out the sprite's offset from its anchor point
            var xAnchorOffset = undefined,
              yAnchorOffset = undefined;
            if (sprite.anchor !== undefined) {
              xAnchorOffset = sprite.width * sprite.anchor.x;
              yAnchorOffset = sprite.height * sprite.anchor.y;
            } else {
              xAnchorOffset = 0;
              yAnchorOffset = 0;
            }

            //Is the sprite rectangular?
            if (!sprite.circular) {
              //Get the position of the sprite's edges using global
              //coordinates
              var left = sprite.gx - xAnchorOffset,
                right = sprite.gx + sprite.width - xAnchorOffset,
                top = sprite.gy - yAnchorOffset,
                bottom = sprite.gy + sprite.height - yAnchorOffset;

              //Find out if the pointer is intersecting the rectangle.
              //`hit` will become `true` if the pointer is inside the
              //sprite's area
              hit = this.x > left && this.x < right && this.y > top && this.y < bottom;
            } else {
              //Is the sprite circular?
              //Find the distance between the pointer and the
              //center of the circle
              var vx = this.x - (sprite.gx + sprite.width / 2 - xAnchorOffset),
                vy = this.y - (sprite.gy + sprite.width / 2 - yAnchorOffset),
                distance = Math.sqrt(vx * vx + vy * vy);

              //The pointer is intersecting the circle if the
              //distance is less than the circle's radius
              hit = distance < sprite.width / 2;
            }
            //Check the value of `hit`
            return hit;
          }
        };

        //Bind the events to the handlers
        //Mouse events
        element.addEventListener('mousemove', pointer.moveHandler.bind(pointer), false);
        element.addEventListener('mousedown', pointer.downHandler.bind(pointer), false);

        //Add the `mouseup` event to the `window` to
        //catch a mouse button release outside of the canvas area
        window.addEventListener('mouseup', pointer.upHandler.bind(pointer), false);

        //Touch events
        element.addEventListener('touchmove', pointer.touchmoveHandler.bind(pointer), false);
        element.addEventListener('touchstart', pointer.touchstartHandler.bind(pointer), false);

        //Add the `touchend` event to the `window` object to
        //catch a mouse button release outside of the canvas area
        window.addEventListener('touchend', pointer.touchendHandler.bind(pointer), false);

        //Disable the default pan and zoom actions on the `canvas`
        element.style.touchAction = 'none';

        //Add the pointer to Tink's global `pointers` array
        this.pointers.push(pointer);

        //Return the pointer
        return pointer;
      }

      //Many of Tink's objects, like pointers, use collision
      //detection using the sprites' global x and y positions. To make
      //this easier, new `gx` and `gy` properties are added to sprites
      //that reference Pixi sprites' `getGlobalPosition()` values.
    },
    {
      key: 'addGlobalPositionProperties',
      value: function addGlobalPositionProperties (sprite) {
        if (sprite.gx === undefined) {
          Object.defineProperty(sprite, 'gx', {
            get: function get () {
              return sprite.getGlobalPosition().x;
            }
          });
        }

        if (sprite.gy === undefined) {
          Object.defineProperty(sprite, 'gy', {
            get: function get () {
              return sprite.getGlobalPosition().y;
            }
          });
        }
      }

      //A method that implments drag-and-drop functionality
      //for each pointer
    },
    {
      key: 'updateDragAndDrop',
      value: function updateDragAndDrop (draggableSprites) {
        //Create a pointer if one doesn't already exist
        if (this.pointers.length === 0) {
          this.makePointer(this.element, this.scale);
        }

        //Loop through all the pointers in Tink's global `pointers` array
        //(there will usually just be one, but you never know)
        this.pointers.forEach(function (pointer) {
          //Check whether the pointer is pressed down
          if (pointer.isDown) {
            //You need to capture the co-ordinates at which the pointer was
            //pressed down and find out if it's touching a sprite

            //Only run pointer.code if the pointer isn't already dragging
            //sprite
            if (pointer.dragSprite === null) {
              //Loop through the `draggableSprites` in reverse to start searching at the bottom of the stack
              for (var i = draggableSprites.length - 1; i > -1; i--) {
                //Get a reference to the current sprite
                var sprite = draggableSprites[i];

                //Check for a collision with the pointer using `hitTestSprite`
                if (pointer.hitTestSprite(sprite) && sprite.draggable) {
                  //Calculate the difference between the pointer's
                  //position and the sprite's position
                  pointer.dragOffsetX = pointer.x - sprite.gx;
                  pointer.dragOffsetY = pointer.y - sprite.gy;

                  //Set the sprite as the pointer's `dragSprite` property
                  pointer.dragSprite = sprite;

                  //The next two lines re-order the `sprites` array so that the
                  //selected sprite is displayed above all the others.
                  //First, splice the sprite out of its current position in
                  //its parent's `children` array
                  var children = sprite.parent.children;
                  children.splice(children.indexOf(sprite), 1);

                  //Next, push the `dragSprite` to the end of its `children` array so that it's
                  //displayed last, above all the other sprites
                  children.push(sprite);

                  //Reorganize the `draggableSpites` array in the same way
                  draggableSprites.splice(draggableSprites.indexOf(sprite), 1);
                  draggableSprites.push(sprite);

                  //Break the loop, because we only need to drag the topmost sprite
                  break;
                }
              }
            } else {
              //If the pointer is down and it has a `dragSprite`, make the sprite follow the pointer's
              //position, with the calculated offset
              pointer.dragSprite.x = pointer.x - pointer.dragOffsetX;
              pointer.dragSprite.y = pointer.y - pointer.dragOffsetY;
            }
          }

          //If the pointer is up, drop the `dragSprite` by setting it to `null`
          if (pointer.isUp) {
            pointer.dragSprite = null;
          }

          //Change the mouse arrow pointer to a hand if it's over a
          //draggable sprite
          draggableSprites.some(function (sprite) {
            if (pointer.hitTestSprite(sprite) && sprite.draggable) {
              if (pointer.visible) pointer.cursor = 'pointer';
              return true;
            } else {
              if (pointer.visible) pointer.cursor = 'auto';
              return false;
            }
          });
        });
      }
    },
    {
      key: 'makeInteractive',
      value: function makeInteractive (o) {
        //The `press`,`release`, `over`, `out` and `tap` methods. They're `undefined`
        //for now, but they can be defined in the game program
        o.press = o.press || undefined;
        o.release = o.release || undefined;
        o.over = o.over || undefined;
        o.out = o.out || undefined;
        o.tap = o.tap || undefined;

        //The `state` property tells you the button's
        //current state. Set its initial state to "up"
        o.state = 'up';

        //The `action` property tells you whether its being pressed or
        //released
        o.action = '';

        //The `pressed` and `hoverOver` Booleans are mainly for internal
        //use in this code to help figure out the correct state.
        //`pressed` is a Boolean that helps track whether or not
        //the sprite has been pressed down
        o.pressed = false;

        //`hoverOver` is a Boolean which checks whether the pointer
        //has hovered over the sprite
        o.hoverOver = false;

        //tinkType is a string that will be set to "button" if the
        //user creates an object using the `button` function
        o.tinkType = '';

        //Set `enabled` to true to allow for interactivity
        //Set `enabled` to false to disable interactivity
        o.enabled = true;

        //Add the sprite to the global `buttons` array so that it can
        //be updated each frame in the `updateButtons method
        this.buttons.push(o);
      }

      //The `updateButtons` method will be called each frame
      //inside the game loop. It updates all the button-like sprites
    },
    {
      key: 'updateButtons',
      value: function updateButtons () {
        var _this3 = this;

        //Create a pointer if one doesn't already exist
        if (this.pointers.length === 0) {
          this.makePointer(this.element, this.scale);
        }

        //Loop through all of Tink's pointers (there will usually
        //just be one)
        this.pointers.forEach(function (pointer) {
          pointer.shouldBeHand = false;

          //Loop through all the button-like sprites that were created
          //using the `makeInteractive` method
          _this3.buttons.forEach(function (o) {
            //Only do this if the interactive object is enabled
            if (o.enabled) {
              //Figure out if the pointer is touching the sprite
              var hit = pointer.hitTestSprite(o);

              //1. Figure out the current state
              if (pointer.isUp) {
                //Up state
                o.state = 'up';

                //Show the first image state frame, if this is a `Button` sprite
                if (o.tinkType === 'button') o.gotoAndStop(0);
              }

              //If the pointer is touching the sprite, figure out
              //if the over or down state should be displayed
              if (hit) {
                //Over state
                o.state = 'over';

                //Show the second image state frame if this sprite has
                //3 frames and it's a `Button` sprite
                if (o.totalFrames && o.totalFrames === 3 && o.tinkType === 'button') {
                  o.gotoAndStop(1);
                }

                //Down state
                if (pointer.isDown) {
                  o.state = 'down';

                  //Show the third frame if this sprite is a `Button` sprite and it
                  //has only three frames, or show the second frame if it
                  //only has two frames
                  if (o.tinkType === 'button') {
                    if (o.totalFrames === 3) {
                      o.gotoAndStop(2);
                    } else {
                      o.gotoAndStop(1);
                    }
                  }
                }

                //Flag this pointer to be changed to a hand
                pointer.shouldBeHand = true;
                //if (pointer.visible) pointer.cursor = "pointer";
                // } else {
                //   //Turn the pointer to an ordinary arrow icon if the
                //   //pointer isn't touching a sprite
                //   if (pointer.visible) pointer.cursor = "auto";

                //Change the pointer icon to a hand
                if (pointer.visible) pointer.cursor = 'pointer';
              } else {
                //Turn the pointer to an ordinary arrow icon if the
                //pointer isn't touching a sprite
                if (pointer.visible) pointer.cursor = 'auto';
              }

              //Perform the correct interactive action

              //a. Run the `press` method if the sprite state is "down" and
              //the sprite hasn't already been pressed
              if (o.state === 'down') {
                if (!o.pressed) {
                  if (o.press) o.press();
                  o.pressed = true;
                  o.action = 'pressed';
                }
              }

              //b. Run the `release` method if the sprite state is "over" and
              //the sprite has been pressed
              if (o.state === 'over') {
                if (o.pressed) {
                  if (o.release) o.release();
                  o.pressed = false;
                  o.action = 'released';
                  //If the pointer was tapped and the user assigned a `tap`
                  //method, call the `tap` method
                  if (pointer.tapped && o.tap) o.tap();
                }

                //Run the `over` method if it has been assigned
                if (!o.hoverOver) {
                  if (o.over) o.over();
                  o.hoverOver = true;
                }
              }

              //c. Check whether the pointer has been released outside
              //the sprite's area. If the button state is "up" and it's
              //already been pressed, then run the `release` method.
              if (o.state === 'up') {
                if (o.pressed) {
                  if (o.release) o.release();
                  o.pressed = false;
                  o.action = 'released';
                }

                //Run the `out` method if it has been assigned
                if (o.hoverOver) {
                  if (o.out) o.out();
                  o.hoverOver = false;
                }
              }
            }
          });

          if (pointer.shouldBeHand) {
            pointer.cursor = 'pointer';
          } else {
            pointer.cursor = 'auto';
          }
        });
      }

      //A function that creates a sprite with 3 frames that
      //represent the button states: up, over and down
    },
    {
      key: 'button',
      value: function button (source) {
        var x = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
        var y = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2];

        //The sprite object that will be returned
        var o = undefined;

        //Is it an array of frame ids or textures?
        if (typeof source[0] === 'string') {
          //They're strings, but are they pre-existing texture or
          //paths to image files?
          //Check to see if the first element matches a texture in the
          //cache
          if (this.TextureCache[source[0]]) {
            //It does, so it's an array of frame ids
            o = this.AnimatedSprite.fromFrames(source);
          } else {
            //It's not already in the cache, so let's load it
            o = this.AnimatedSprite.fromImages(source);
          }
        } else if (source[0] instanceof this.Texture) {
          //If the `source` isn't an array of strings, check whether
          //it's an array of textures
          //Yes, it's an array of textures.
          //Use them to make a AnimatedSprite o
          o = new this.AnimatedSprite(source);
        }

        //Add interactive properties to the button
        this.makeInteractive(o);

        //Set the `tinkType` to "button"
        o.tinkType = 'button';

        //Position the button
        o.x = x;
        o.y = y;

        //Return the new button sprite
        return o;
      }

      //Run the `udpate` function in your game loop
      //to update all of Tink's interactive objects
    },
    {
      key: 'update',
      value: function update () {
        //Update the drag and drop system
        if (this.draggableSprites.length !== 0) this.updateDragAndDrop(this.draggableSprites);

        //Update the buttons and button-like interactive sprites
        if (this.buttons.length !== 0) this.updateButtons();
      }

      /*
    `keyboard` is a method that listens for and captures keyboard events. It's really
    just a convenient wrapper function for HTML `keyup` and `keydown` events so that you can keep your application code clutter-free and easier to write and read.
     Here's how to use the `keyboard` method. Create a new keyboard object like this:
    ```js
    let keyObject = keyboard(asciiKeyCodeNumber);
    ```
    It's one argument is the ASCII key code number of the keyboard key
    that you want to listen for. [Here's a list of ASCII key codes you can
    use](http://www.asciitable.com).
    Then assign `press` and `release` methods to the keyboard object like this:
    ```js
    keyObject.press = () => {
      //key object pressed
    };
    keyObject.release = () => {
      //key object released
    };
    ```
    Keyboard objects also have `isDown` and `isUp` Boolean properties that you can use to check the state of each key. 
    */
    },
    {
      key: 'keyboard',
      value: function keyboard (keyCode) {
        var key = {};
        key.code = keyCode;
        key.isDown = false;
        key.isUp = true;
        key.press = undefined;
        key.release = undefined;

        //The `downHandler`
        key.downHandler = function (event) {
          if (event.keyCode === key.code) {
            if (key.isUp && key.press) key.press();
            key.isDown = true;
            key.isUp = false;
          }
          event.preventDefault();
        };

        //The `upHandler`
        key.upHandler = function (event) {
          if (event.keyCode === key.code) {
            if (key.isDown && key.release) key.release();
            key.isDown = false;
            key.isUp = true;
          }
          event.preventDefault();
        };

        //Attach event listeners
        window.addEventListener('keydown', key.downHandler.bind(key), false);
        window.addEventListener('keyup', key.upHandler.bind(key), false);

        //Return the key object
        return key;
      }

      //`arrowControl` is a convenience method for updating a sprite's velocity
      //for 4-way movement using the arrow directional keys. Supply it
      //with the sprite you want to control and the speed per frame, in
      //pixels, that you want to update the sprite's velocity
    },
    {
      key: 'arrowControl',
      value: function arrowControl (sprite, speed) {
        if (speed === undefined) {
          throw new Error('Please supply the arrowControl method with the speed at which you want the sprite to move');
        }

        var upArrow = this.keyboard(38),
          rightArrow = this.keyboard(39),
          downArrow = this.keyboard(40),
          leftArrow = this.keyboard(37);

        //Assign key `press` methods
        leftArrow.press = function () {
          //Change the sprite's velocity when the key is pressed
          sprite.vx = -speed;
          sprite.vy = 0;
        };
        leftArrow.release = function () {
          //If the left arrow has been released, and the right arrow isn't down,
          //and the sprite isn't moving vertically:
          //Stop the sprite
          if (!rightArrow.isDown && sprite.vy === 0) {
            sprite.vx = 0;
          }
        };
        upArrow.press = function () {
          sprite.vy = -speed;
          sprite.vx = 0;
        };
        upArrow.release = function () {
          if (!downArrow.isDown && sprite.vx === 0) {
            sprite.vy = 0;
          }
        };
        rightArrow.press = function () {
          sprite.vx = speed;
          sprite.vy = 0;
        };
        rightArrow.release = function () {
          if (!leftArrow.isDown && sprite.vy === 0) {
            sprite.vx = 0;
          }
        };
        downArrow.press = function () {
          sprite.vy = speed;
          sprite.vx = 0;
        };
        downArrow.release = function () {
          if (!upArrow.isDown && sprite.vx === 0) {
            sprite.vy = 0;
          }
        };
      }
    },
    {
      key: 'scale',
      get: function get () {
        return this._scale;
      },
      set: function set (value) {
        this._scale = value;

        //Update scale values for all pointers
        this.pointers.forEach(function (pointer) {
          return (pointer.scale = value);
        });
      }
    }
  ]);

  return Tink;
})();
//# sourceMappingURL=tink.js.map

function scaleToWindow (canvas, backgroundColor) {
  var scaleX, scaleY, scale, center;

  //1. Scale the canvas to the correct size
  //Figure out the scale amount on each axis
  scaleX = window.innerWidth / canvas.offsetWidth;
  scaleY = window.innerHeight / canvas.offsetHeight;

  //Scale the canvas based on whichever value is less: `scaleX` or `scaleY`
  scale = Math.min(scaleX, scaleY);
  canvas.style.transformOrigin = '0 0';
  canvas.style.transform = 'scale(' + scale + ')';

  //2. Center the canvas.
  //Decide whether to center the canvas vertically or horizontally.
  //Wide canvases should be centered vertically, and
  //square or tall canvases should be centered horizontally
  if (canvas.offsetWidth > canvas.offsetHeight) {
    if (canvas.offsetWidth * scale < window.innerWidth) {
      center = 'horizontally';
    } else {
      center = 'vertically';
    }
  } else {
    if (canvas.offsetHeight * scale < window.innerHeight) {
      center = 'vertically';
    } else {
      center = 'horizontally';
    }
  }

  //Center horizontally (for square or tall canvases)
  var margin;
  if (center === 'horizontally') {
    margin = (window.innerWidth - canvas.offsetWidth * scale) / 2;
    canvas.style.marginTop = 0 + 'px';
    canvas.style.marginBottom = 0 + 'px';
    canvas.style.marginLeft = margin + 'px';
    canvas.style.marginRight = margin + 'px';
  }

  //Center vertically (for wide canvases)
  if (center === 'vertically') {
    margin = (window.innerHeight - canvas.offsetHeight * scale) / 2;
    canvas.style.marginTop = margin + 'px';
    canvas.style.marginBottom = margin + 'px';
    canvas.style.marginLeft = 0 + 'px';
    canvas.style.marginRight = 0 + 'px';
  }

  //3. Remove any padding from the canvas  and body and set the canvas
  //display style to "block"
  canvas.style.paddingLeft = 0 + 'px';
  canvas.style.paddingRight = 0 + 'px';
  canvas.style.paddingTop = 0 + 'px';
  canvas.style.paddingBottom = 0 + 'px';
  canvas.style.display = 'block';

  //4. Set the color of the HTML body background
  document.body.style.backgroundColor = backgroundColor;

  //Fix some quirkiness in scaling for Safari
  var ua = navigator.userAgent.toLowerCase();
  if (ua.indexOf('safari') != -1) {
    if (ua.indexOf('chrome') > -1) {
      // Chrome
    } else {
      // Safari
      //canvas.style.maxHeight = "100%";
      //canvas.style.minHeight = "100%";
    }
  }

  //5. Return the `scale` value. This is important, because you'll nee this value
  //for correct hit testing between the pointer and sprites
  return scale;
}

export { Bump, Tink, keyboard, hitTestRectangle };
